/**
 * Copyright (c) 2010 Simple Graph Tools contributors. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * $Id$
 */
package org.sgt.coredef.util;

import java.util.HashSet;
import java.util.Set;

import org.sgt.coredef.Chain;
import org.sgt.coredef.CoredefFactory;
import org.sgt.coredef.DecomposableNode;
import org.sgt.coredef.Edge;
import org.sgt.coredef.Node;
import org.sgt.coredef.ProxyNode;

/**
 * A Default GraphSequencer policy.
 * 
 * @author Anne BUTTIGHOFFER
 * 
 */
public abstract class DefaultGraphSequencerPolicy implements
		IGraphSequencerPolicy {

	/**
	 * This default implementation considers as a fork node any {@link Node}
	 * having more than one next node in the given {@link Chain} set.
	 * 
	 * @param node
	 *            {@link Node}.
	 * @param allChains
	 *            {@link Set<Chain>}.
	 * @return true only if given node is to be considered as a fork.
	 * @see IGraphSequencerPolicy#isJoin(Node)
	 */
	public final boolean isFork(final Node node, final Set<Chain> allChains) {
		Set<Node> nextNodes = new HashSet<Node>();
		for (Chain chain : allChains) {
			Node next = ChainHelper.nextNode(chain, node);
			if (next != null) {
				nextNodes.add(next);
			}
		}
		return (nextNodes.size() > 1);
	}

	/**
	 * This default implementation considers as a join node any {@link Node}
	 * having more than one previous in the given {@link Chain} set.
	 * 
	 * @param node
	 *            {@link Node}.
	 * @param allChains
	 *            {@link Set<Chain>}.
	 * @return true only if given node is to be considered as a join.
	 * @see IGraphSequencerPolicy#isJoin(Node)
	 */
	public final boolean isJoin(final Node node, final Set<Chain> allChains) {
		Set<Node> prevNodes = new HashSet<Node>();
		for (Chain chain : allChains) {
			Node prev = ChainHelper.previousNode(chain, node);
			if (prev != null) {
				prevNodes.add(prev);
			}
		}
		return (prevNodes.size() > 1);
	}

	/**
	 * Determines whether this SequencerPolicy should consider current
	 * {@link Node} as a decision node in the context of given Set of Chains
	 * (i.e. both join and fork).
	 * 
	 * @param node
	 *            {@link Node}.
	 * @param allChains
	 *            {@link Set<Chain>}.
	 * @return true only if node has to be considered a Decision by current
	 *         Policy.
	 */
	public final boolean isDecision(final Node node
			, final Set<Chain> allChains) {
		return (isJoin(node, allChains) && isFork(node, allChains));
	}

	/**
	 * This default methods returns
	 * <UL>
	 * <LI>true for any {@link DecomposableNode} or any {@link ProxyNode} which
	 * represents a {@link DecomposableNode}</LI>
	 * <LI>false otherwise.</LI>
	 * </UL>
	 * 
	 * @param node
	 *            {@link Node}
	 * @return see above.
	 */
	public final boolean isSequencable(final Node node) {
		if (node instanceof DecomposableNode) {
			return true;
		}
		if (node instanceof ProxyNode) {
			ProxyNode proxy = (ProxyNode) node;
			Node pointed = proxy.getRepresentedObject();
			return (pointed instanceof DecomposableNode);
		} else {
			return false;
		}
	}

/**
	 * This default implementation creates a {@link DecomposableEdge}
	 * (the only concrete {@link Edge available at coredef level...).
	 * @param decompNode
	 *            {@link DecomposableNode}.
	 * @param source
	 *            {@link Destination}.
	 * @return an edge connecting source and decompNode.
	 * @see IGraphSequencerPolicy#createEdge(Node, DecomposableNode)
	 */
	public final Edge createEdge(final Node source,
			final DecomposableNode decompNode) {
		Edge edge = CoredefFactory.eINSTANCE.createDecomposableEdge();
		edge.setSource(source);
		edge.setDestination(decompNode);
		return edge;
	}

/**
	 * This default implementation creates a {@link DecomposableEdge}
	 * (the only concrete {@link Edge available at coredef level...).
	 * @param decompNode
	 *            {@link DecomposableNode}.
	 * @param destination
	 *            {@link Destination}.
	 * @return an edge connecting decompNode and destination.
	 * @see IGraphSequencerPolicy#createEdge(DecomposableNode, Node)
	 */
	public final Edge createEdge(final DecomposableNode decompNode,
			final Node destination) {
		Edge edge = CoredefFactory.eINSTANCE.createDecomposableEdge();
		edge.setSource(decompNode);
		edge.setDestination(destination);
		return edge;
	}

}
